#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <iostream>
#include <fstream>
#include <sys/select.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>


class server
{
public:
    server(short port)
        :_port(port)
    {
        _listenfd = socket(AF_INET,SOCK_STREAM,0);
        if(_listenfd == -1)
        {
            std::cout << "socket error!" << std::endl;
            exit(-1);
        }
        int val = 1;
        setsockopt(_listenfd,SOL_SOCKET,SO_REUSEADDR,&val,sizeof(val));

        sockaddr_in server;
        memset(&server,0,sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_port);
        server.sin_addr.s_addr = INADDR_ANY;
        int n = bind(_listenfd,(const sockaddr *)&server,sizeof(server));
        if(n == -1)
        {
           std::cout << "bind error!" << std::endl;
            exit(-1);
        }
        
        n = listen(_listenfd,5);
        if(n == -1)
        {
            std::cout << "listen error!" << std::endl;
            exit(-1);
        }

        FD_ZERO(&_reads);
        FD_SET(_listenfd,&_reads);
        _maxfd = _listenfd;
    }

    void start()
    {
        while(true)
        {
            fd_set tmp = _reads;
            struct timeval timeout;
            timeout.tv_sec = 5;
            timeout.tv_usec = 0;
            
            int ret = select(_maxfd+1,&tmp,nullptr,nullptr,&timeout);
            if(ret == 0)
            {
                printf("Time Out!\n");
                continue;
            }
            if(ret == -1)
            {
                printf("select error!\n");
                break;
            }
            // 监视的文件描述符有事件发生了
            for(int i=0;i<_maxfd+1;i++)
            {
                if(FD_ISSET(i,&tmp))
                {
                    if(i == _listenfd)
                    {
                        sockaddr_in client;
                        socklen_t len = sizeof(client);
                        int connfd = accept(i,(sockaddr*)&client,&len);
                        FD_SET(connfd,&_reads);
                        if(_maxfd < connfd) _maxfd = connfd;
                        printf("新的客户端连接！%d\n",connfd);
                    }
                    else 
                    {
                        char buffer[1024] = {0};
                        int n = read(i,buffer,sizeof(buffer)-1);
                        if(n > 0)
                        {
                            write(i,buffer,n);
                        }
                        else if(n == 0)
                        {
                            FD_CLR(i,&_reads);
                            close(i);
                            printf("客户端下线！%d\n",i);
                        }
                    }
                }
            }
            
        }
        close(_listenfd);
    }
protected:
    int _listenfd;
    short _port;
    fd_set _reads;
    int _maxfd;// 当前最大的文件描述符
};

int main(int argc,char *argv[])
{
    if(argc != 2)
    {
        printf("Usage: %s <port>\n",argv[0]);
        return -1;
    }
    short port = atoi(argv[1]);
    server s(port);
    s.start();
    return 0;
}